//package com.caishi.lkx.common.util;
//
//import cn.hutool.core.collection.CollUtil;
//import cn.hutool.core.map.MapUtil;
//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
//import com.zzw.common.entity.BSService;
//import com.zzw.common.entity.IBSEntity;
//import lombok.extern.slf4j.Slf4j;
//import org.elasticsearch.action.bulk.BulkRequest;
//import org.elasticsearch.action.bulk.BulkResponse;
//import org.elasticsearch.action.index.IndexRequest;
//import org.elasticsearch.action.search.SearchRequest;
//import org.elasticsearch.action.search.SearchResponse;
//import org.elasticsearch.client.RequestOptions;
//import org.elasticsearch.client.RestHighLevelClient;
//import org.elasticsearch.client.indices.CreateIndexRequest;
//import org.elasticsearch.client.indices.CreateIndexResponse;
//import org.elasticsearch.client.indices.PutMappingRequest;
//import org.elasticsearch.common.settings.Settings;
//import org.elasticsearch.common.text.Text;
//import org.elasticsearch.common.xcontent.XContentBuilder;
//import org.elasticsearch.common.xcontent.json.JsonXContent;
//import org.elasticsearch.index.query.*;
//import org.elasticsearch.index.reindex.DeleteByQueryRequest;
//import org.elasticsearch.index.reindex.UpdateByQueryRequest;
//import org.elasticsearch.rest.RestStatus;
//import org.elasticsearch.script.Script;
//import org.elasticsearch.script.ScriptType;
//import org.elasticsearch.search.SearchHit;
//import org.elasticsearch.search.SearchHits;
//import org.elasticsearch.search.builder.SearchSourceBuilder;
//import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
//import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
//import org.elasticsearch.search.sort.SortOrder;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.Resource;
//import java.io.IOException;
//import java.io.Serializable;
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//
///**
// * @author by liyihua
// * @date : 2022-05-26 16:10
// */
//@Slf4j
//@Component
//public class EsSearchUtil<T extends IBSEntity<T, ID>, ID extends Serializable, S extends BSService<T, ID>> {
//    @Resource
//    private RestHighLevelClient client;
//
//    public void createIndex(String indexName, List<String> searchFields) {
//        CreateIndexRequest indexRequest = new CreateIndexRequest(indexName);
//        //设置索引库的相关属性
//        Settings settings = Settings.builder()
//                .put("number_of_shards", 1)//设置分片数量
//                .put("number_of_replicas", 0)//设置副本数量
//                .build();
//        indexRequest.settings(settings);
//        try {
//            CreateIndexResponse response = client.indices().create(indexRequest, RequestOptions.DEFAULT);
//            createMappings(indexName, searchFields);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
//
//    /**
//     * 创建index，相当于创建表
//     *
//     * @param indexName
//     * @param searchFields 需要搜索的字段需要设置mapping分词器
//     * @return
//     */
//    private Boolean createMappings(String indexName, List<String> searchFields) {
//        PutMappingRequest putMappingRequest = new PutMappingRequest(indexName);
//        try {
//            XContentBuilder builder = JsonXContent.contentBuilder();
//            builder.startObject()
//                    .startObject("properties");
//            for (String searchField : searchFields) {
//                builder.startObject(searchField)
//                        .field("type", "text")
//                        //ik_max_word 分词精度更高
//                        .field("analyzer", "ik_max_word")
//                        .endObject();
//            }
//            builder.endObject().endObject();
//            putMappingRequest.source(builder);
//            client.indices().putMapping(putMappingRequest, RequestOptions.DEFAULT);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return true;
//    }
//
//    /**
//     * 搜索
//     *
//     * @param page
//     * @param indexName
//     * @param fieldName
//     * @param keyword
//     * @param service
//     * @return
//     * @throws IOException
//     */
//    public Map<String, Object> searchByKeyword(Page<T> page, String indexName, String fieldName, String keyword, S service) throws IOException {
//        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery(fieldName, keyword);
//        SearchHits hits = queryHotals(indexName, matchQuery, null, page.getSize(), page.getCurrent(), fieldName);
//        return coverHits(hits, page, service, fieldName);
//    }
//
//
//    /**
//     * 多条件同时满足搜索
//     *
//     * @param page
//     * @param conditionsMap 查询字段
//     * @param highField     高亮字段
//     * @param indexName
//     * @param service
//     * @return
//     * @throws IOException
//     */
//    public Map<String, Object> esByMustConditions(Page<T> page, Map<String, String> conditionsMap, String indexName, S service, String... highField) throws IOException {
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        for (Map.Entry<String, String> entry : conditionsMap.entrySet()) {
//            boolQueryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
//        }
//        SearchHits hits = queryHotals(indexName, boolQueryBuilder, null, page.getSize(), page.getCurrent(), highField);
//        return coverHits(hits, page, service, highField);
//    }
//
//    /**
//     * 多条件满足其一即可搜索
//     *
//     * @param page
//     * @param conditionsMap
//     * @param indexName
//     * @param highField
//     * @param service
//     * @return
//     * @throws IOException
//     */
//    public Map<String, Object> esByShouldConditions(Page<T> page, Map<String, String> conditionsMap, String indexName, S service, String... highField) throws IOException {
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        for (Map.Entry<String, String> entry : conditionsMap.entrySet()) {
//            boolQueryBuilder.should(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
//        }
//        SearchHits hits = queryHotals(indexName, boolQueryBuilder, null, page.getSize(), page.getCurrent(), highField);
//        return coverHits(hits, page, service, highField);
//    }
//
//    /**
//     * @param page
//     * @param shouldMap      可以满足的条件
//     * @param mustMap        必须满足的条件
//     * @param filterMap      过滤
//     * @param existColumnIds 不为空的字段
//     * @param indexName
//     * @param service
//     * @param highField
//     * @return
//     * @throws IOException
//     */
//    public Map<String, Object> esByConditions(Page<T> page, Map<String, Map<String, Object>> shouldMap, Map<String, Object> mustMap, Map<String, Object> filterMap, List<String> existColumnIds, Map<String, SortOrder> sort, String indexName, S service, String... highField) throws IOException {
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        for (Map.Entry<String, Map<String, Object>> entry : shouldMap.entrySet()) {
//            Map<String, Object> map = entry.getValue();
//            boolQueryBuilder.should(QueryBuilders.matchQuery(entry.getKey(), map.get("value")).minimumShouldMatch("4<80%").boost(Long.parseLong(map.get("boost") != null ? map.get("boost").toString() : "1")).analyzer("ik_smart"));
//        }
//        for (Map.Entry<String, Object> entry : mustMap.entrySet()) {
//            boolQueryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()).minimumShouldMatch("4<80%").analyzer("ik_smart"));
//        }
//        if (filterMap != null) {
//            for (Map.Entry<String, Object> entry : filterMap.entrySet()) {
//                boolQueryBuilder.filter(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
//            }
//        }
//
//        //该字段不能为null
//        if (existColumnIds != null) {
//            for (String existColumnId : existColumnIds) {
//                boolQueryBuilder.must(QueryBuilders.existsQuery(existColumnId));
//            }
//        }
//
//        SearchHits hits = queryHotals(indexName, boolQueryBuilder, sort, page.getSize(), page.getCurrent(), highField);
//        return coverHits(hits, page, service, highField);
//    }
//
//    public Map<String, Object> esByConditions(Page<T> page, String value, List<String> fieldNames, Map<String, Object> filterMap, List<String> existColumnIds, String indexName, S service, String... highField) throws IOException {
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(value, fieldNames.toArray(String[]::new)).type(MultiMatchQueryBuilder.Type.BEST_FIELDS).analyzer("ik_max_word").minimumShouldMatch("4<70%");
//        boolQueryBuilder.must(multiMatchQueryBuilder);
//        for (Map.Entry<String, Object> entry : filterMap.entrySet()) {
//            boolQueryBuilder.filter(QueryBuilders.termQuery(entry.getKey(), entry.getValue()));
//        }
//        //该字段不能为null
//        if (existColumnIds != null) {
//            for (String existColumnId : existColumnIds) {
//                boolQueryBuilder.must(QueryBuilders.existsQuery(existColumnId));
//            }
//        }
//        SearchHits hits = queryHotals(indexName, boolQueryBuilder, null, page.getSize(), page.getCurrent(), highField);
//        return coverHits(hits, page, service, highField);
//    }
//
//    private Map<String, Object> coverHits(SearchHits hits, Page<T> page, S service, String... highField) {
//        List<ID> ids = new ArrayList<>();
//        Map<String, Map<String, String>> replaceMap = new HashMap<>();
//        for (SearchHit hit : hits) {
//            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//            ids.add((ID) sourceAsMap.get("id").toString());
//            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//            for (String s : highField) {
//                HighlightField highlightTitle = highlightFields.get(s);
//                if (highlightTitle != null) {
//                    org.elasticsearch.common.text.Text[] fragments = highlightTitle.fragments();
//                    StringBuilder str = new StringBuilder();
//                    for (Text text : fragments) {
//                        str.append(text);
//                    }
//                    //将高亮替换为原来的内容
//                    if (replaceMap.containsKey(sourceAsMap.get("id").toString())) {
//                        Map<String, String> map = replaceMap.get(sourceAsMap.get("id").toString());
//                        map.put(s, str.toString());
//                    } else {
//                        replaceMap.put(sourceAsMap.get("id").toString(), MapUtil.<String, String>builder().put(s, str.toString()).build());
//                    }
//                }
//            }
//        }
//        if (CollUtil.isNotEmpty(ids)) {
//            List<T> list = new ArrayList<>();
//            ids.forEach(id -> list.add(service.getById(id)));
//            page.setTotal(null != hits.getTotalHits() ? hits.getTotalHits().value : 0);
//            page.setRecords(list);
//
//        }
//        return MapUtil.<String, Object>builder()
//                .put("replaceMap", replaceMap)
//                .put("page", page).build();
//    }
//
//    private SearchHits queryHotals(String indexName, QueryBuilder queryBuilder, Map<String, SortOrder> sortMap, Long size, Long current, String... fieldNames) throws IOException {
//        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        sourceBuilder.query(queryBuilder);
//        sourceBuilder.from((current.intValue() - 1) * size.intValue());
//        sourceBuilder.size(size.intValue());
//        if (null != sortMap) {
//            for (String sortField : sortMap.keySet()) {
//                sourceBuilder.sort(sortField, sortMap.get(sortField));
//            }
//        }
//        //过滤查询分数为0的数据
//        sourceBuilder.minScore(0.01f);
//        //高亮
//        HighlightBuilder highlightBuilder = new HighlightBuilder();
//        for (String fieldName : fieldNames) {
//            highlightBuilder.field(fieldName);
//        }
//        highlightBuilder.requireFieldMatch(false);
//        // 高亮返回数据长度，小于题干或选项长度，将会报错，替换不了原文
//        highlightBuilder.fragmentSize(1000);
//        highlightBuilder.numOfFragments(0);
//        highlightBuilder.preTags("<span style='color:red;'>")
//                .postTags("</span>");
//        sourceBuilder.highlighter(highlightBuilder);
//        return getHits(indexName, sourceBuilder);
//
//    }
//
//    /**
//     * 插入
//     *
//     * @param indexName
//     * @param map       只存关键字段，实体
//     * @return
//     * @throws IOException
//     */
//    public Boolean insertDoc(String indexName, Map<String, Object> map) throws IOException {
//        IndexRequest indexRequest = new IndexRequest();
//        indexRequest.index(indexName);
//        indexRequest.source(map);
//        try {
//            client.index(indexRequest, RequestOptions.DEFAULT);
//        } catch (IOException e) {
////          if (!(e.getMessage()).contains("200 OK")) {
//            if (!(e.getMessage()).contains("201 Created")) {
//                throw e;
//            }
//        }
//        return true;
//    }
//
//
//    /**
//     * 更新
//     *
//     * @param indexName
//     * @param map
//     * @return
//     * @throws IOException
//     */
//    public Boolean updateDoc(String indexName, Map<String, Object> map) throws IOException {
//        UpdateByQueryRequest request = new UpdateByQueryRequest(indexName);
//        //设置版本冲突时继续执行
//        request.setConflicts("proceed");
//        //设置更新完成后刷新索引 ps很重要如果不加可能数据不会实时刷新
//        request.setRefresh(true);
//        request.setQuery(new MatchQueryBuilder("id", map.get("id")));
//        //"ctx._source.city = params.city;ctx._source.province = params.province;ctx._source.country = params.country"
//        StringBuilder builder = new StringBuilder();
//        for (Map.Entry<String, Object> entry : map.entrySet()) {
//            builder.append("ctx._source.").append(entry.getKey()).append("=params.").append(entry.getKey()).append(";");
//        }
//        Script script = new Script(ScriptType.INLINE, "painless", builder.toString(), map);
//        request.setScript(script);
//        try {
//            client.updateByQuery(request, RequestOptions.DEFAULT);
//        } catch (IOException e) {
//            if (!(e.getMessage()).contains("201 Created")) {
//                throw e;
//            }
//        }
//        return true;
//    }
//
//
//    /**
//     * 删除
//     *
//     * @param indexName
//     * @param map
//     * @return
//     * @throws IOException
//     */
//    public Boolean deleteDoc(String indexName, Map<String, Object> map) throws IOException {
//        DeleteByQueryRequest request = new DeleteByQueryRequest(indexName);
//        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("id", map.get("id"));
//        //设置版本冲突时继续执行
//        request.setConflicts("proceed");
//        //设置更新完成后刷新索引 ps很重要如果不加可能数据不会实时刷新
//        request.setRefresh(true);
//        request.setQuery(queryBuilder);
//        try {
//            client.deleteByQuery(request, RequestOptions.DEFAULT);
//        } catch (IOException e) {
//            if (!(e.getMessage()).contains("201 Created")) {
//                throw e;
//            }
//        }
//        return true;
//    }
//
//    /**
//     * 根据id搜索文档
//     *
//     * @param indexName
//     * @param id
//     * @return
//     * @throws IOException
//     */
//    public Boolean selectDocById(String indexName, String id) throws IOException {
//        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("id", id);
//        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        sourceBuilder.query(queryBuilder);
//        SearchRequest searchRequest = new SearchRequest(indexName);
//        searchRequest.source(sourceBuilder);
//        try {
//            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
//            return RestStatus.OK == search.status();
//        } catch (IOException e) {
//            if ((e.getMessage()).contains("200 OK")) {
//                return true;
//            }
//            log.error(e.getMessage());
//        }
//        return false;
//    }
//
//    SearchHits getHits(String indexName, SearchSourceBuilder sourceBuilder) throws IOException {
//        SearchRequest searchRequest = new SearchRequest(indexName);
//        searchRequest.source(sourceBuilder);
//        try {
//            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
//            return search.getHits();
//        } catch (IOException e) {
//            if (!(e.getMessage()).contains("200 OK")) {
//                throw e;
//            }
//            e.printStackTrace();
//        }
//        return null;
//    }
//
//
//    private HighlightBuilder getHighlightBuilder() {
//        HighlightBuilder highlightBuilder = new HighlightBuilder();
//        highlightBuilder.preTags("<font color='#EE7700'>");
//        highlightBuilder.postTags("</font>");
//
//        /*  * 可选:unified,plain和fvh
//         * unified : 使用Lucene的统一highlighter。
//         * 这个突出显示器将文本分成句子，并使用BM25算法对单个句子进行评分，
//         * 就好像它们是语料库中的文档一样。它还支持准确的短语和多项（模糊，前缀，正则表达式）突出显示
//         *
//         *plain highlighter最适合在单一领域突出简单的查询匹配。
//         * 为了准确反映查询逻辑，它会创建一个微小的内存索引，
//         * 并通过Lucene的查询执行计划程序重新运行原始查询条件，
//         * 以访问当前文档的低级匹配信息。对于需要突出显示的每个字段和每个文档都会
//         * 重复此操作。如果要在复杂查询的大量文档中突出显示很多字段，
//         * 我们建议使用unified highlighter postings或term_vector字段
//         *
//         *fvh highlighter使用Lucene的Fast Vector highlighter。此突出显示器可用于映射中term_vector设置为的
//         * 字段with_positions_offsets。Fast Vector highlighter
//         **/
//        highlightBuilder.highlighterType("unified");
//
////         * 这只高亮字段,我这里设置为要查询的字段一致
//
//        highlightBuilder.
//                field("companyCode").field("loginType")
//                .field("userId").field("appid").field("loginIp")
//                .field("unitCode").field("id").field("userType");
//
//        highlightBuilder.requireFieldMatch(false);
//        /*
//         * fragmentSize  设置要显示出来的fragment文本判断的长度，默认是100
//         * numOfFragments 代表要显示几处高亮(可能会搜出多段高亮片段)。默认是5
//         * noMatchSize  即使字段中没有关键字命中，也可以返回一段文字，该参数表示从开始多少个字符被返回
//         */
//        //highlightBuilder.fragmentSize(size).numOfFragments(3).noMatchSize(100);
//
//        return highlightBuilder;
//    }
//
//    /**
//     * 批量新增 （删除跟修改也可以根据这个改）
//     *
//     * @param indexName
//     * @param maps
//     * @return
//     */
//    public Boolean batchInsert(String indexName, List<Map<String, Object>> maps) {
//        try {
//
//            // 1、创建批量操作请求
//            BulkRequest request = new BulkRequest();
//            maps.forEach(r -> request.add(new IndexRequest(indexName).source(r)));
//
//            /*
//            批量跟新，批量删除
//            request.add(new DeleteRequest("mess", "_doc", "3"));
//            request.add(new UpdateRequest("mess", "_doc", "2")
//                    .doc(XContentType.JSON,"other", "test"));
//            request.add(new IndexRequest("mess", "_doc", "4")
//                    .source(XContentType.JSON,"field", "baz"));
//            */
//
//            // 2、可选的设置
//            /*
//            request.timeout("2m");
//            request.setRefreshPolicy("wait_for");
//            request.waitForActiveShards(2);
//            */
//
//            //3、发送请求，异步方式发送批量操作请求
//            // java.io.IOException: Unable to parse response body for Response{requestLine=POST /_bulk?timeout=1m HTTP/1.1, host=http://127.0.0.1:9200, response=HTTP/1.1 200 OK}
//            //实际数据插入成功，但是会报上述，异步会直接进到onFailure
//         /*   ActionListener<BulkResponse> listener = new ActionListener<BulkResponse>() {
//                @Override
//                public void onResponse(BulkResponse bulkResponse) {
//                    log.info("批量插入es成功，index："+indexName+"data:{"+maps.toString()+"}");
//                }
//
//                @Override
//                public void onFailure(Exception e) {
//                    log.info("批量插入es失败{}，"+e+"index："+indexName+"data:{"+maps.toString()+"};");
//                }
//            };
//            client.bulkAsync(request,RequestOptions.DEFAULT, listener);*/
//
//            //4、同步请求
//            BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
//            //处理响应
//          /*  if (bulkResponse != null) {
//                for (BulkItemResponse bulkItemResponse : bulkResponse) {
//                    DocWriteResponse itemResponse = bulkItemResponse.getResponse();
//                    if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.INDEX
//                            || bulkItemResponse.getOpType() == DocWriteRequest.OpType.CREATE) {
//                        IndexResponse indexResponse = (IndexResponse) itemResponse;
//                        // 新增成功的处理
//                    }
//                     else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.UPDATE) {
//                    UpdateResponse updateResponse = (UpdateResponse) itemResponse;
//                    // 修改成功的处理
//                } else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.DELETE) {
//                    DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
//                    // 删除成功的处理
//                }
//            }
//        }*/
//        } catch (IOException e) {
//            if (!(e.getMessage()).contains("200 OK")) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//
//    //先要建index 的 mapping ，把要搜索的字段设置分词器 ik_max_word分词精度高  ik_smart分词精度低
//    /*PUT es_question
//    {
//        "mappings": {
//        "properties": {
//            "stem": {
//                "analyzer": "ik_max_word",
//                        "type": "text"
//            },
//            "quOption": {
//                "analyzer": "ik_max_word",
//                        "type": "text"
//            },
//            "answerDesc": {
//                "analyzer": "ik_max_word",
//                        "type": "text"
//            },
//            "answer": {
//                "analyzer": "ik_max_word",
//                        "type": "text"
//            }
//        }
//    }
//    }*/
//}
